<?php
/**
 * CommonModel.class.php    通用模型
 *
 * @author                  mashanling(msl-138@163.com)
 * @date                    2012-05-27 19:25:32
 * @last modify             2012-09-29 08:59:01 by mashanling
 */

!defined('ROOT') && exit('Access Denied');
//sleep(1);
class CommonModel extends Model {
    protected $module = null;
    /**
     * 构造函数
     *
     * @return void 无返回值
     */
    function __construct($name = null) {

        if (!empty($this->db_fields)) {//字段
            $db_fields = array();

            foreach ($this->db_fields as $field => $item) {

                if (isset($item['validate'])) {//自动验证
                    $validator = $item['validate'];

                    if (is_array($validator)) {//多种验证规则

                        foreach ($validator as $v) {
                            $this->setValidate($field, $v);
                        }
                    }
                    else {
                        $this->setValidate($field, $validator);
                    }
                }

                $db_fields[] = $field;
            }

            $this->fields = $db_fields;//表字段
        }

        if (!empty($this->auto)) {//自动完成 by mashanling on 2012-09-26 11:16:37
            foreach ($this->auto as $field => $v) {
                $this->setAutoOperate($field, $v);
            }
        }

        parent::__construct($name ? $name : MODULE_NAME);
    }//end __construct

	/**
     * 添加日志
     *
     * @param string $content   日志内容
     * @param int    $log_type  日志类型，默认0，sql错误
     *
     * @return void 无返回值
     */
    function addLog($content = '', $log_type = LOG_TYPE_SQL_ERROR) {
        $data = array(
            'content'  => $log_type == LOG_TYPE_SQL_ERROR && !$content ? $this->_sql() . '<br />' . $this->getDbError() : $content,
            'log_type' => $log_type,
        );

        $log_model = D('Log');
        $log_model->autoOperation($data, Model::MODEL_INSERT);
        $log_model->add($data);
    }

    /**
     * 验证自动创建数据是否成功
     *
     * @return mixed 如果验证成功，返回true，否则返回错误信息
     */
    function checkCreate($method = 'create') {

        if (REQUEST_METHOD != 'POST' && !isset($_GET['__get'])) {
            $this->addLog(L(empty($this->data[$this->getPk()]) ? 'ADD' : 'EDIT') . L('MODULE_NAME,FAILURE,%.,_DATA_TYPE_INVALID_'), LOG_TYPE_INVALID_PARAM);
            return L('_DATA_TYPE_INVALID_');
        }

        $result = true;

        if (!$this->$method()) {
            $error = $this->getError();
            $result = is_array($error) ? join('<br />', $error) : $error;
            $this->addLog(__function__ . '<br />' . $result, LOG_TYPE_VALIDATE_FORM_ERROR);
        }

        return $result;
    }

    /**
     * 验证 string1/string2 格式
     *
     * @author       mashanling(msl-138@163.com)
     * @date         2012-09-28 11:46:12
     * @last modify  2012-09-28 11:46:12 by mashanling
     *
     * @return bool 格式正确，返回true，否则返回false
     */
    protected function checkExplodeNumericFormat($string, $delimiter = '/') {
        $arr = explode($delimiter, $string);

        return isset($arr[1]) && is_numeric($arr[0]) && is_numeric($arr[1]);
    }

    /**
     * 检测是否是叶，即是否有子节点
     *
     * @author       mashanling(msl-138@163.com)
     * @date         2012-08-19 21:39:32
     * @last modify  2012-08-19 21:39:32 by mashanling
     *
     * @param int $pk_value 主键值
     *
     * @return bool 有子节点，返回false，否则返回true
     */
    function checkIsLeaf($pk_value, $where) {
        static $data = null;
        $data = $data === null ? $this->field('parent_id')->group('parent_id')->where($where)->select(array(), 'parent_id') : $data;

        if (!$data) {
            return true;
        }

        return !array_key_exists($pk_value, $data);
    }

    /**
     * 检测值长度是否在指定范围内
     *
     * @param string $value 待检测值
     * @param string $name  检测名称
     * @param int    $min   最小值
     * @param int    $max   最大值
     *
     * @return mixed 检测成功，返回true，否则返回长度错误提示信息
     */
    protected function checkLength($value, $name, $min, $max = null) {

        if (!is_numeric($max)) {//指定长度
            return $this->check($value, $min, 'length') ? true : L($name . ',LENGTH,MUST,EQ') . $min;
        }

        if (!$min) {//超出最大长度
            $msg = sprintf(L('GT_LENGTH'), L($name), $max);
        }
        elseif (!$max) {//小于最小长度
            $msg = sprintf(L('LT_LENGTH'), L($name), $min);
        }
        else {
            $msg = sprintf(L('CONSTRAIN_LENGTH'), L($name), $min, $max);
        }

        return $this->check($value, $min . ',' . $max, 'length') ? true : $msg;
    }

	/**
     * 验证验证码是否正确
     *
     * @author       mashanling
     * @date         2012-06-25 16:34:04
     * @last modify  2012-09-29 08:58:49 by mashanling
     *
     * @param string $code   验证码
     * @param string $module 验证码模块
     *
     * @return mixed 如果正确，返回true，否则返回提示信息
     */
    protected function checkVerifycode($code, $module) {
        $verifycode_setting = get_verifycode_setting($module);

        if (!$verifycode_setting['enable']) {//未开启验证码
            return true;
        }

        if ($code === '') {//未输入验证码
            return false;
        }

        if (($checked = check_verifycode($code, $module)) === true) {//转至check_verifycode验证 by mashanling on 2012-07-13 16:54:54
            $this->_checkVerifycode = true;//通过验证码检测 by mashanling on 2012-07-02 09:55:53
            return true;
        }

        $this->addLog(session(SESSION_VERIFY_CODE) . '(' . $verifycode_setting['order'] . ') => ' . $code, LOG_TYPE_VERIFYCODE_ERROR);

        return L('VERIFY_CODE,NOT_CORRECT');
    }

    /**
     * 双重加密密码
     *
     * @author       mashanling
     * @date         2012-07-02 10:39:04
     * @last modify  2012-07-02 10:39:04 by mashanling
     *
     * @param string $password
     *
     * @return string 双重加密后的密码
     */
    protected function encryptPassword($password) {
        return md5(md5($password));
    }

	/**
     * 获取管理员id
     *
     * @author       mashanling(msl-138@163.com)
     * @date         2012-07-09 11:01:05
     * @last modify  2012-07-09 11:01:05 by mashanling
     *
     * @return int 管理 员id
     */
    protected function getAdminId() {
        $admin_info = session('__admin__');

        return $admin_info ? $admin_info['admin_id'] : 0;
    }

	/**
     * 获取管理员姓名
     *
     * @author       mashanling(msl-138@163.com)
     * @date         2012-07-09 11:11:54 by mashanling
     * @last modify  2012-07-09 11:11:54 by mashanling
     *
     * @return string 管理 员姓名
     */
    protected function getAdminName() {
        $admin_info = session('__admin__');

        return $admin_info ? $admin_info['realname'] : '';
    }

    /**
     * 获取自动完成
     *
     * @author       mashanling(msl-138@163.com)
     * @date         2012-07-14 09:56:10
     * @last modify  2012-07-14 09:56:10 by mashanling
     *
     * @param mixed $field 字段
     * @param bool  $set_property 是否setProperty，默认true，设置
     *
     * @return array 自动完成
     */
    function getAutoOperate($field, $set_property = true) {
        $field_arr = is_array($field) ? $field : explode(',', $field);
        $auto      = array();

        foreach ($field_arr as $field) {
            $auto[] = $this->_auto[$field];
        }

        $set_property && $this->setProperty('_auto', $auto);

        return $auto;
    }

    /**
     * 获取表数据缓存
     *
     * @param int    $id     数据id
     * @param string $name   文件名，默认模块名称
     * @param bool   $reload 是否重新加载，默认false
     * @param string $path   缓存路径,默认CACHE_PATH
     *
     * @return mixed 如果不指定id，返回全部缓存，如果指定id并指定id缓存存在，返回指定id缓存，否则返回false
     */
    function getCache($id = 0, $name = null, $reload = false, $path = DATA_PATH) {
        $data = F($name ? $name : $this->getModelName(), '', $path, $reload);

        if ($id) {
            return isset($data[$id]) ? $data[$id] : false;
        }

        return $data;
    }

    /**
     * 获取checkbox值
     *
     * @param int    $value     值，默认0，即不勾选时
     * @param string $separator 一组复选框值连接符 by mashanling on 2012-09-13 08:38:06
     *
     * @return mixed checkbox数值或用$separator连接的字符串
     */
    protected function getCheckboxValue($value = 0, $separator = ',') {

        if (func_num_args() == 2) {//一组复选框,name[],name[]...
            return join($separator, $value);
        }

        return is_numeric($value) ? intval($value) : '';
    }

    /**
     * 获取当前页面url
     *
     * @return string 当前页面url
     */
    protected function getPageUrl() {
        return REQUEST_METHOD . ' ' . to_website_url(urldecode($_SERVER['REQUEST_URI']));
    }

    /**
     * 获取来路页面url
     *
     * @return string 如果有来路，返回来路页面url，否则返回空字符串
     */
    protected function getRefererUrl() {
        return isset($_SERVER['HTTP_REFERER']) ? urldecode($_SERVER['HTTP_REFERER']) : '';
    }

    /**
     * 动态获取自动验证规则
     *
     * @author       mashanling(msl-138@163.com)
     * @date         2012-07-14 09:36:58
     * @last modify  2012-09-26 11:07:23 by mashanling
     *
     * @param mixed $field        字段
     * @param bool  $set_property 是否setProperty，默认true，设置
     *
     * @return array 自动验证规则
     */
    function getValidate($field, $set_property = true) {
        $field_arr  = is_array($field) ? $field : explode(',', $field);
        $validation = array();

        foreach ($field_arr as $field) {

            if (isset($this->db_fields[$field]['validate'])) {//自动验证
                $validator = $this->db_fields[$field]['validate'];

                if (is_array($validator)) {//多种验证规则

                    foreach ($validator as $v) {
                        $validation[] = $this->setValidate($field, $v, $set_property);;
                    }
                }
                else {
                    $validation[] = $this->setValidate($field, $validator, $set_property);;
                }
            }
        }

        $set_property && $this->setProperty('_validate', $validation);

        return $validation;
    }

    /**
     * 设置自动完成规则
     *
     * @author       mashanling(msl-138@163.com)
     * @date         2012-09-26 08:58:58
     * @last modify  2012-09-26 08:58:58 by mashanling
     *
     * @param string $field_name 字段名
     * @param mixed  $auto       自动完成规则
     *
     * @return object 本类实例
     */
    protected function setAutoOperate($field_name, $auto) {
        $auto = is_array($auto) ? $auto : explode('#', $auto);

        if (count($auto) < 4) {//简写形式method#params
            $method = $auto[0];//验证方法
            $params = isset($auto[1]) ? $auto[1] :  null;//额外参数

            switch($method) {
                case 'setPassword'://密码array('password', 'setPassword', Model::MODEL_BOTH, 'callback', 'data')
                case 'unsigned'://unsigned类型array('sort_order', 'unsigned', Model::MODEL_BOTH, 'callback', 'data'),
                case 'getCheckboxValue': //复选框array('is_restrict', 'getCheckboxValue', Model::MODEL_BOTH, 'callback')
                case 'strtotime'://strtotime array('lock_start_time', 'strtotime', Model::MODEL_BOTH, 'callback')
                    $auto  = array($method, Model::MODEL_BOTH, 'callback');
                    isset($params) ? $auto[] = $params : '';
                    break;

                case 'get_user_id'://用户id array('user_id', 'get_user_id', Model::MODEL_BOTH, 'function')
                case 'get_client_ip'://用户ip  array('admin_ip', 'get_client_ip', Model::MODEL_INSERT, 'function', '1'),
                case 'gmtime'://时间戳array('last_time', 'gmtime', Model::MODEL_BOTH, 'function'),
                    $auto  = array($method, $params == 'insert' ? Model::MODEL_INSERT : Model::MODEL_BOTH, 'function');
                    isset($params) ? $auto[] = $params : '';
                    break;

                case 'string'://字符串填充array('controller', MODULE_NAME, Model::MODEL_BOTH, 'string')
                    $auto = array($method, Model::MODEL_BOTH, 'string');
                    break;

                /*case 'getAdminId'://管理员idarray('admin_name', 'getAdminId', Model::MODEL_INSERT, 'callback')
                case 'getAdminName'://管理员姓名array('page_url', 'getAdminName', Model::MODEL_INSERT, 'callback')
                case 'getPageUrl'://当前页面array('page_url', 'getPageUrl', Model::MODEL_INSERT, 'callback'),
                case 'getRefererUrl'://来路页面array('referer_url', 'getRefererUrl', Model::MODEL_INSERT, 'callback')*/
                default:
                    $auto  = array($method, $params == 'insert' ? Model::MODEL_INSERT : Model::MODEL_BOTH, 'callback');
                    break;
            }//end switch
        }//end if

        array_unshift($auto, $field_name);
        $this->_auto[$field_name] = $auto;

        return $this;
    }//end setAutoOperate

    /**
     * 设置表数据缓存
     *
     * @param array  $data  手工设置缓存数据
     * @param string $name  文件名，默认模块名称
     * @param mixed  $model 模型，默认null，当前模型
     * @param string $path  缓存路径,默认CACHE_PATH
     *
     * @return object 本类实例
     */
    function setCache($data = array(), $name = null, $model = null, $path = DATA_PATH) {
        if ($model === null) {
            $model = $this;
        }
        else {
            $model = is_string($model) ? D($model) : $model;
        }

        F($name ? $name : $this->getModelName(), $data ? $data : $model->select(array(), $model->getPk()), $path);

        return $model;
    }

    /**
     * 设置自动验证规则
     *
     * @author       mashanling(msl-138@163.com)
     * @date         2012-09-26 08:58:58
     * @last modify  2012-09-26 08:58:58 by mashanling
     *
     * @param string $field_name   字段名
     * @param mixed  $validate     自动验证规则
     * @param bool   $set_property 是否setProperty，默认true，设置
     *
     * @return array 自动验证规则
     */
    protected function setValidate($field_name, $validate, $set_property = true) {
        $validate = is_array($validate) ? $validate : explode('#', $validate);

        if (count($validate) < 4) {//简写形式method#msg#params
            $method     = $validate[0];//验证方法
            $msg        = $validate[1];//提示信息
            $msg        = strpos($msg, '%') === 0 ? substr($msg, 1) : L($msg);
            $params     = isset($validate[2]) ? $validate[2] :  null;//额外参数

            switch($method) {
                case 'return'://只过滤array('', '{%PERMISSION,DATA,INVALID}', Model::MUST_VALIDATE, 'return')
                    $validate = array('', $msg . L('DATA,INVALID'), Model::MUST_VALIDATE, $method);
                    break;

                case 'unsigned': //unsigned类型array('', '{%PRIMARY_KEY,DATA,INVALID}', Model::MUST_VALIDATE, 'unsigned')
                    $min      = isset($params) ? $params : -1;//PLEASE_SELECT,PARENT_MENU
                    $validate = array($min, $msg . (strpos($validate[1], ',') ? '' : L('MUST,GT') . $min), Model::MUST_VALIDATE, $method);
                    break;

                case 'notblank'://不允许为空array('', '{%PLEASE_ENTER,MODULE_NAME_MENU}', Model::MUST_VALIDATE, 'notblank')
                    $validate = array('', L('PLEASE_ENTER') . $msg, Model::MUST_VALIDATE, $method);
                    break;

                case 'validate_dir'://路径验证array('validate_dir', 'css路径非法', Model::VALUE_VALIDATE, 'function', Model::MODEL_BOTH, 'css路径|ROOT')
                    $validate = array($method, $msg . L('DATA,INVALID'), Model::VALUE_VALIDATE, 'function', Model::MODEL_BOTH, $msg . (isset($params) ? '|' . $params : ''));
                    break;

                case 'checkLength'://验证长度 'checkLength', '{%USERNAME,DATA,INVALID}', Model::VALUE_VALIDATE, 'callback', Model::MODEL_BOTH, array('USERNAME', 0, 20)
                    $params        = explode('|', $params);
                    $must_validate = $params[1] == 'must';//必须验证
                    $params[0]     = $validate[1];
                    $validate      = array('checkLength', $msg . L('DATA,INVALID'), $must_validate ? Model::MUST_VALIDATE : Model::VALUE_VALIDATE, 'callback', Model::MODEL_BOTH, $params);
                    break;

                default://array('checkUsername', '{%PLEASE_ENTER,USERNAME}', Model::MUST_VALIDATE, 'callback', model::MODEL_BOTH, 'data')
                    $validate = array($method, $msg, Model::MUST_VALIDATE, 'callback', Model::MODEL_BOTH, $params);
                    break;
            }//end switch
        }//end if

        array_unshift($validate, $field_name);
        $set_property ? $this->_validate[] = $validate : '';

        return $validate;
    }//end setValidate

    /**
     * 将时间表达式转化成unix时间戳
     *
     * @author       mashanling(msl-138@163.com)
     * @date         2012-09-05 15:54:10
     * @last modify  2012-09-09 14:45:13 by mashanling
     *
     * @param string $datetime 时间表达式
     *
     * @return 转化成功，返回转化的时间戳，否则返回0
     */
    protected function strtotime($datetime) {
        return $datetime && ($datetime = local_strtotime($datetime)) ? $datetime : 0;
    }

    /**
     * 检测时间区域是否存在
     *
     * @author       mashanling(msl-138@163.com)
     * @date         2012-09-11 13:18:01
     * @last modify  2012-09-11 13:18:01 by mashanling
     *
     * @param string $timezone 区域
     *
     * @return 存在返回true,否则返回false
     */
    protected function timezone($timezone) {
        try {
            new DateTimeZone($timezone);
            return true;
        }
        catch (Exception $e) {
            return false;
        }
    }

    /**
     * 释放值，无须如库
     *
     * @author       mashanling(msl-138@163.com)
     * @date         2012-09-10 15:40:08
     * @last modify  2012-09-10 15:40:08 by mashanling
     *
     * @param mixed $value 值,默认false
     *
     * @return bool false
     */
    protected function unsetValue($value = false) {
        return false;
    }

    /**
     * 回调unsigned字段，如排序sort_order=-1时，unset掉，否则mysql会产生Out of range value for column 'sort_order' at row 1警告
     *
     * @author       mashanling(msl-138@163.com)
     * @date         2012-09-10 16:03:55
     * @last modify  2012-09-13 12:39:03 by mashanling
     *
     * @param mixed $value 值,默认0
     * @param array  $data _POST数据
     * @param int   $min   最小值，默认0
     *
     * @return mixed 小于0,新增返回false,编辑返回主键值,否则返回其值
     */
    protected function unsigned($value = 0, $data = array(), $min = 0) {
        $pk_value = empty($data[$this->pk]) ? 0 : $data[$this->pk];

        return $value < $min ? ($pk_value ? $pk_value : false) : $value;
    }
}